package com.yswy.common.wxOrder.V3.wxPayV3Callback;

import cn.hutool.json.JSONUtil;
import com.aliyun.oss.ServiceException;
import com.google.gson.Gson;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.yswy.common.utils.StringUtils;
import com.yswy.common.wxOrder.V3.WxPayConfigV3;
import com.yswy.common.wxOrder.V3.wxPayV3Refund.WechatRefundCallback;
import com.yswy.common.wxOrder.V3.wxPayV3Refund.WxchatCallbackRefundData;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.Map;


import java.util.function.Consumer;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;


import javax.servlet.http.HttpServletRequest;


import javax.servlet.http.HttpServletResponse;

@Slf4j
public class WxPayCallbackUtil {






    /**
     * 微信支付创建订单回调方法
     * @param verifier 证书
     * @param wxPayConfig 微信配置
     * @param businessCallback 回调方法，用于处理业务逻辑
     * @return json格式的string数据，直接返回给微信
     */
    public static String wxPaySuccessCallback(HttpServletRequest request, HttpServletResponse response,
                                              Verifier verifier, WxPayConfigV3 wxPayConfig,
                                              Consumer<WxchatCallbackSuccessData> businessCallback) {
        Gson gson = new Gson();

        // 1.处理通知参数
        final String body = readData(request);
        HashMap<String, Object> bodyMap = gson.fromJson(body, HashMap.class);

        // 2.签名验证
        if (verifier != null){
            WechatPayValidatorForRequest wechatForRequest = new WechatPayValidatorForRequest(verifier, body, (String) bodyMap.get("id"));
            try {
                if (!wechatForRequest.validate(request)) {
                    // 通知验签失败
                    response.setStatus(500);
                    final HashMap<String, Object> map = new HashMap<>();
                    map.put("code", "ERROR");
                    map.put("message", "通知验签失败");
                    return gson.toJson(map);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

//微信回调解密前:{summary=支付成功, event_type=TRANSACTION.SUCCESS, create_time=2023-08-30T17:30:10+08:00, resource={original_type=transaction, algorithm=AEAD_AES_256_GCM, ciphertext=TDOPz+1B67jDvaz+C+q1DJJz1rz1Zgyp2xvgGMSfW2Yz2N3k8dKSrOb8RQYF706m5HIbj//+RW8YwcOh7MLHiQoPkV4jh4F6tPHm0wHCr1kDmHFjpaid5HzV08ndSogKOALInpBknnuS2BszvwTamzYviz2zqY9r0l571cf2awdOJc3C0NxQrqS0qupJobZnJY786GyDsaXUAPDhBQOCjjGNM3vLpLYSkTT3RfMvj5SOr64KMz2XYOb9YWuw4uvdaPvxDzqVkKFxaaxZ8CKH3ZoUwYmarttlHqbxLVEwCOpyZPFqJum/k+BqGHsknkvEzF7ZbsQuBuUP3JxGJB+6wHpRiylMauRC5YtHLHtsVP6E95ok1aM1fIdRL+d6NK2YZjVRRkrZZ4+au+1I/ifYYqhwoNqbenfmceI2WKnDrrvXH5gziTIqAXMbYyz8Y2sMn3X5RIV2NU/gbdhKcKPViEMKXRGZw8nu/Fcz+flEU/EQVRM22q4G+dwlpYGHrkwMRp14NtOxM9Mdm6+wfWFDYxeH8pCxb0uWihxNZ3XCKTJXsgQyFNh1XfANsQHNrvgsVOwpOCECsfMg, associated_data=transaction, nonce=4lEc8s5djEGV}, resource_type=encrypt-resource, id=0a0fecdb-d7e3-56c1-9ddf-5e56bc76d4a6}
//微信回调解密后:{transaction_id=4200001956202308305852172330, amount={total=10.0, payer_total=10.0, currency=CNY, payer_currency=CNY}, mchid=1645167139, out_trade_no=WX202308301730011107300352, trade_state=SUCCESS, bank_type=OTHERS, appid=wxd5b74c5ae85bc51e, trade_state_desc=支付成功, trade_type=APP, attach=, success_time=2023-08-30T17:30:10+08:00, payer={openid=oEjsW6brrhmpagFU-V03c7ZYmWLg}}
//: callbackData:WxchatCallbackSuccessData(orderId=WX202308301730011107300352, transactionId=4200001956202308305852172330, tradestate=SUCCESS, successTime=2023-08-30 17:30:10, tradetype=APP, totalMoney=0.100)
//: 微信支付返回的信息：WxchatCallbackSuccessData(orderId=WX202308301730011107300352, transactionId=4200001956202308305852172330, tradestate=SUCCESS, successTime=2023-08-30 17:30:10, tradetype=APP, totalMoney=0.100)
        // 3.获取明文数据
        String plainText = decryptFromResource(bodyMap,wxPayConfig);
        HashMap<String,Object> plainTextMap = null ;
        try {
            plainTextMap = gson.fromJson(plainText, HashMap.class);
        }catch (Exception e){
            response.setStatus(200);
            final HashMap<String, Object> resultMap = new HashMap<>();
            resultMap.put("code", "SUCCESS");
            resultMap.put("message", "成功");
            return gson.toJson(resultMap);
        }
        log.info("微信回调解密后:{}",plainTextMap);
        // 4.封装微信返回的数据
        WxchatCallbackSuccessData callbackData = new WxchatCallbackSuccessData();
        callbackData.setSuccessTime(String.valueOf(plainTextMap.get("success_time")));
        callbackData.setOrderId(String.valueOf(plainTextMap.get("out_trade_no")));
        callbackData.setTransactionId(String.valueOf(plainTextMap.get("transaction_id")));
        callbackData.setTradestate(String.valueOf(plainTextMap.get("trade_state")));
        callbackData.setTradetype(String.valueOf(plainTextMap.get("trade_type")));
        String amount = String.valueOf(plainTextMap.get("amount"));
        HashMap<String,Object> amountMap = gson.fromJson(amount, HashMap.class);
        String total = String.valueOf(amountMap.get("total"));
        callbackData.setTotalMoney(new BigDecimal(total).movePointLeft(2));

        /**
         * 交易状态
         * SUCCESS：支付成功
         * REFUND：转入退款
         * NOTPAY：未支付
         * CLOSED：已关闭
         * REVOKED：已撤销（付款码支付）
         * USERPAYING：用户支付中（付款码支付）
         * PAYERROR：支付失败(其他原因，如银行返回失败)
         */
        if ("SUCCESS".equals(callbackData.getTradestate()) ||
                "CLOSED".equals(callbackData.getTradestate()) ||
                "PAYERROR".equals(callbackData.getTradestate()) ) {
            // 执行业务逻辑
            businessCallback.accept(callbackData);
        }

        // 5.成功应答
        response.setStatus(200);
        final HashMap<String, Object> resultMap = new HashMap<>();
        resultMap.put("code", "SUCCESS");
        resultMap.put("message", "成功");
        return gson.toJson(resultMap);
    }



    /**
     * 微信支付申请退款回调方法
     *
     * @param verifier       证书
     * @param wxPayConfig    微信配置
     * @param refundCallback 回调方法，用于处理业务逻辑，包含退款成功处理于退款失败处理
     * @return json格式的string数据，直接返回给微信
     */
    public static String wxPayRefundCallback(HttpServletRequest request, HttpServletResponse response
            , Verifier verifier, WxPayConfigV3 wxPayConfig, WechatRefundCallback refundCallback) {
        Gson gson = new Gson();

        // 1.处理通知参数
        final String body = readData(request);
        HashMap<String, Object> bodyMap = gson.fromJson(body, HashMap.class);

        // 2.签名验证
        if (verifier != null){
            WechatPayValidatorForRequest wechatForRequest = new WechatPayValidatorForRequest(verifier, body, (String) bodyMap.get("id"));
            try {
                if (!wechatForRequest.validate(request)) {
                    // 通知验签失败
                    response.setStatus(500);
                    final HashMap<String, Object> map = new HashMap<>();
                    map.put("code", "ERROR");
                    map.put("message", "通知验签失败");
                    return gson.toJson(map);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }



        // 3.获取明文数据
        String plainText = decryptFromResource(bodyMap, wxPayConfig);
        HashMap<String, Object> plainTextMap = gson.fromJson(plainText, HashMap.class);
        log.info("退款plainTextMap:{}", plainTextMap);
        // 4.封装微信返回的数据
        WxchatCallbackRefundData refundData = getRefundCallbackData(plainTextMap);


        if ("SUCCESS".equals(refundData.getStatus())) {
            // 执行业务逻辑
            refundCallback.success(refundData);
        } else {
            // 特殊情况退款失败业务处理，退款到银行发现用户的卡作废或者冻结了，导致原路退款银行卡失败，可前往商户平台-交易中心，手动处理此笔退款
            refundCallback.fail(refundData);
        }

        // 5.成功应答
        response.setStatus(200);
        final HashMap<String, Object> resultMap = new HashMap<>();
        resultMap.put("code", "SUCCESS");
        resultMap.put("message", "成功");
        return gson.toJson(resultMap);
    }

    private static WxchatCallbackRefundData getRefundCallbackData(HashMap<String, Object> plainTextMap) {
        Gson gson = new Gson();
        WxchatCallbackRefundData refundData = new WxchatCallbackRefundData();
        String successTime = String.valueOf(plainTextMap.get("success_time"));
        if (StringUtils.isNoneBlank(successTime)) {
            refundData.setSuccessTime(successTime);
        }
        refundData.setOrderId(String.valueOf(plainTextMap.get("out_trade_no")));
        refundData.setRefundId(String.valueOf(plainTextMap.get("out_refund_no")));
        refundData.setTransactionId(String.valueOf(plainTextMap.get("transaction_id")));
        refundData.setTransactionRefundId(String.valueOf(plainTextMap.get("refund_id")));
        refundData.setChannel(String.valueOf(plainTextMap.get("channel")));
        final String status = String.valueOf(plainTextMap.get("refund_status"));
        refundData.setStatus(status);
        String amount = String.valueOf(plainTextMap.get("amount"));
        HashMap<String, Object> amountMap = gson.fromJson(amount, HashMap.class);
        String refundMoney = String.valueOf(amountMap.get("refund"));
        refundData.setRefundMoney(new BigDecimal(refundMoney).movePointLeft(2));
        log.info("refundData:{}", refundData);
        return refundData;
    }

    /**
     * 对称解密
     */
    private static String decryptFromResource(HashMap<String, Object> bodyMap, WxPayConfigV3 wxPayConfig) {
        // 通知数据
        Map<String, String> resourceMap = (Map) bodyMap.get("resource");
        // 数据密文
        String ciphertext = resourceMap.get("ciphertext");
        // 随机串
        String nonce = resourceMap.get("nonce");
        // 附加数据
        String associateData = resourceMap.get("associated_data");
        AesUtil aesUtil = new AesUtil(wxPayConfig.getKey().getBytes(StandardCharsets.UTF_8));
        try {
            return aesUtil.decryptToString(associateData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
            throw new ServiceException("解密失败");
        }
    }



    /**
     * 分析数据
     *
     * @param request 请求
     * @return {@link Map}<{@link String}, {@link Object}>
     */
    public static Map<String, Object> analysisData(HttpServletRequest request) {
        String body = readData(request);
        return JSONUtil.toBean(body, HashMap.class);
    }

    /**
     * 将通知参数转化为字符串
     */
    public static String readData(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            StringBuilder result = new StringBuilder();
            br = request.getReader();
            for (String line; (line = br.readLine()) != null; ) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append(line);
            }
            return result.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
